#include "mongoc_const.h"
#include "mongoc_data.h"
#include <stdio.h>

int raob_mongoc_insert_platform(int *cid, const char *name, float *lon, float *lat, float *z) {
  if (*cid >= nbundle) {
    return FORTRAN_MONGO_MEMORY_ERROR;
  }
  struct MongoObjectBundle *bundle = &bundles[*cid];

  bson_t *doc;
  bson_error_t error;

  doc = BCON_NEW(
    "sid", BCON_UTF8(name),
    "location", "{",
      "type", "Point",
      "coordinates", "[", BCON_DOUBLE(*lon), BCON_DOUBLE(*lat), "]",
    "}",
    "z", BCON_DOUBLE(*z)
  );

  if (!mongoc_collection_insert_one(bundle->col, doc, NULL, NULL, &error)) {
    bson_destroy(doc);
    if (error.code == MONGOC_ERROR_DUPLICATE_KEY) {
      return FORTRAN_MONGO_INSERT_DUP_WARN;
    } else {
      printf("%s\n", name);
      printf("[Error]: raob_mongoc_insert_platform: %s\n", error.message);
      return FORTRAN_MONGO_INSERT_ERROR;
    }
  }

  bson_destroy(doc);

  return FORTRAN_MONGO_NO_ERROR;
}

int raob_mongoc_insert_record(int *cid, const char *name, double *timestamp,
                    float *  sfc_ta, float *  sfc_p, float *  sfc_sh, float *  sfc_ua, float *  sfc_va,
    int * man_nlev, float ** man_ta, float ** man_p, float ** man_sh, float ** man_ua, float ** man_va,
    int *sigt_nlev, float **sigt_ta, float **sigt_p, float **sigt_sh, float **sigt_ua, float **sigt_va,
    int *sigw_nlev,                  float **sigw_p,                  float **sigw_ua, float **sigw_va,
    int *trop_nlev, float **trop_ta, float **trop_p, float **trop_sh, float **trop_ua, float **trop_va) {
  if (*cid >= nbundle) {
    return FORTRAN_MONGO_MEMORY_ERROR;
  }
  struct MongoObjectBundle *bundle = &bundles[*cid];

  bson_t *doc;
  bson_error_t error;
 
  doc = BCON_NEW(
    "sid"   , BCON_UTF8(name),
    "time"  , BCON_DATE_TIME(*timestamp * 1000), // UTC time
    "sfc_ta", BCON_DOUBLE(*sfc_ta),
    "sfc_p" , BCON_DOUBLE(*sfc_p ),
    "sfc_sh", BCON_DOUBLE(*sfc_sh),
    "sfc_ua", BCON_DOUBLE(*sfc_ua),
    "sfc_va", BCON_DOUBLE(*sfc_va)
  );
  bson_t child;
  char key[3];
  if (*man_nlev > 0) {
    // ---------------------------------------------------------------------------
    //                                  man_ta
    bson_init(&child);
    bson_append_array_begin(doc, "man_ta", -1, &child);
    for (int k = 0; k < *man_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*man_ta)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  man_p
    bson_init(&child);
    bson_append_array_begin(doc, "man_p", -1, &child);
    for (int k = 0; k < *man_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*man_p)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  man_sh
    bson_init(&child);
    bson_append_array_begin(doc, "man_sh", -1, &child);
    for (int k = 0; k < *man_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*man_sh)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  man_ua
    bson_init(&child);
    bson_append_array_begin(doc, "man_ua", -1, &child);
    for (int k = 0; k < *man_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*man_ua)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  man_va
    bson_init(&child);
    bson_append_array_begin(doc, "man_va", -1, &child);
    for (int k = 0; k < *man_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*man_va)[k]);
    }
    bson_append_array_end(doc, &child);
  }
  if (*sigt_nlev > 0) {
    // ---------------------------------------------------------------------------
    //                                  sigt_ta
    bson_init(&child);
    bson_append_array_begin(doc, "sigt_ta", -1, &child);
    for (int k = 0; k < *sigt_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigt_ta)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigt_p 
    bson_init(&child);
    bson_append_array_begin(doc, "sigt_p", -1, &child);
    for (int k = 0; k < *sigt_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigt_p)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigt_sh
    bson_init(&child);
    bson_append_array_begin(doc, "sigt_sh", -1, &child);
    for (int k = 0; k < *sigt_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigt_sh)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigt_ua
    bson_init(&child);
    bson_append_array_begin(doc, "sigt_ua", -1, &child);
    for (int k = 0; k < *sigt_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigt_ua)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigt_va
    bson_init(&child);
    bson_append_array_begin(doc, "sigt_va", -1, &child);
    for (int k = 0; k < *sigt_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigt_va)[k]);
    }
    bson_append_array_end(doc, &child);
  }
  if (*sigw_nlev > 0) {
    // ---------------------------------------------------------------------------
    //                                  sigw_p 
    bson_init(&child);
    bson_append_array_begin(doc, "sigw_p", -1, &child);
    for (int k = 0; k < *sigw_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigw_p)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigw_ua
    bson_init(&child);
    bson_append_array_begin(doc, "sigw_ua", -1, &child);
    for (int k = 0; k < *sigw_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigw_ua)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  sigw_va
    bson_init(&child);
    bson_append_array_begin(doc, "sigw_va", -1, &child);
    for (int k = 0; k < *sigw_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*sigw_va)[k]);
    }
    bson_append_array_end(doc, &child);
  }
  if (*trop_nlev > 0) {
    // ---------------------------------------------------------------------------
    //                                  trop_ta
    bson_init(&child);
    bson_append_array_begin(doc, "trop_ta", -1, &child);
    for (int k = 0; k < *trop_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*trop_ta)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  trop_p 
    bson_init(&child);
    bson_append_array_begin(doc, "trop_p", -1, &child);
    for (int k = 0; k < *trop_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*trop_p)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  trop_sh
    bson_init(&child);
    bson_append_array_begin(doc, "trop_sh", -1, &child);
    for (int k = 0; k < *trop_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*trop_sh)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  trop_ua
    bson_init(&child);
    bson_append_array_begin(doc, "trop_ua", -1, &child);
    for (int k = 0; k < *trop_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*trop_ua)[k]);
    }
    bson_append_array_end(doc, &child);
    // ---------------------------------------------------------------------------
    //                                  trop_va
    bson_init(&child);
    bson_append_array_begin(doc, "trop_va", -1, &child);
    for (int k = 0; k < *trop_nlev; ++k) {
      sprintf(key, "%d", k);
      BSON_APPEND_DOUBLE(&child, key, (*trop_va)[k]);
    }
    bson_append_array_end(doc, &child);
  }

  if (!mongoc_collection_insert_one(bundle->col, doc, NULL, NULL, &error)) {
    bson_destroy(doc);
    if (error.code == MONGOC_ERROR_DUPLICATE_KEY) {
      return FORTRAN_MONGO_INSERT_DUP_WARN;
    } else {
      printf("%s\n", name);
      printf("[Error]: raob_mongoc_insert_record: %s\n", error.message);
      return FORTRAN_MONGO_INSERT_ERROR;
    }
  }

  bson_destroy(doc);

  return FORTRAN_MONGO_NO_ERROR;

}
